package com.fattyCorps.game.service.player;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fattyCorps.common.mgr.Cfg;
import com.fattyCorps.common.mgr.GlobalConfig;
import com.fattyCorps.common.obj.config.MainGuanqianMould;
import com.fattyCorps.common.obj.config.PlayerExpMould;
import com.fattyCorps.common.packet.dto.DItem;
import com.fattyCorps.common.util.DateUtils;
import com.fattyCorps.game.cache.PlayerCache;
import com.fattyCorps.game.exception.game.CreatePlayerErrorException;
import com.fattyCorps.game.mapper.PlayerMapper;
import com.fattyCorps.game.mgr.event.EEvent;
import com.fattyCorps.game.mgr.event.EventMgr;
import com.fattyCorps.game.obj.cache.PlayerInfo;
import com.fattyCorps.game.obj.db.DtoPlayer;
import com.fattyCorps.game.obj.db.PlayerData;
import com.fattyCorps.game.obj.msg.SRet;
import com.fattyCorps.game.obj.msg.server.player.SGetHangAward;
import com.fattyCorps.game.obj.msg.server.player.SGetInfo;
import com.fattyCorps.game.service.HeroService;
import com.fattyCorps.game.service.ItemService;
import com.fattyCorps.game.service.MailService;
import com.fattyCorps.game.util.GameJwtUtils;
import com.fattyCorps.game.util.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class PlayerService {

    @Autowired
    PlayerMapper playerMapper;

    @Autowired
    PlayerCache playerCache;

    @Autowired
    HeroService heroService;

    @Autowired
    ItemService itemService;

    @Autowired
    MailService mailService;

    @Autowired
    GlobalConfig globalConfig;

    @Autowired
    EventMgr eventMgr;

    public int createPlayer(Integer accountId) {
        SimpleDateFormat df = new SimpleDateFormat(DateUtils.YYYY_MM_DD_HH_MM_SS);
        String initTimeStr = df.format(Integer.valueOf(0));

        int curTime = TimeUtils.unixTime();

        Integer initHead = globalConfig.getIntValue(GlobalConfig.PLAYER_INIT_HEAD);
        Integer initLv = globalConfig.getIntValue(GlobalConfig.PLAYER_INIT_LV);
        Integer initCoin = globalConfig.getIntValue(GlobalConfig.PLAYER_INIT_COIN);
        Integer initGuanqia = globalConfig.getIntValue(GlobalConfig.PLAYER_INIT_GUANQIA);

        PlayerInfo playerInfo = new PlayerInfo();
        playerInfo.setNickname("acc" + accountId);
        playerInfo.setAccountId(accountId);

        PlayerData playerData = new PlayerData();
        playerData.setHead(initHead);
        playerData.setLv(initLv);
        playerData.setCoin(initCoin);
        playerData.setGuanqia(initGuanqia);
        playerData.setGlobalMailId(0);
        playerData.setCreateTime(curTime);
        playerData.setLastHangTime(curTime);
        playerInfo.setPlayerData(playerData);

        playerInfo.getPlayerData().setLastFreeLotteryTime(initTimeStr);

        // 派发消息，其他模块相应，处理创建初始化工作
        eventMgr.sendEvent(EEvent.EVENT_ON_PLAYER_CREATE, playerInfo);

        DtoPlayer dto = playerInfo.getDto();
        try {
            playerMapper.insert(dto);
        } catch (Exception e) {
            log.error("getInfo error: accountId" + accountId);
            e.printStackTrace();
            throw new CreatePlayerErrorException();
        }

        return dto.getId();
    }

    // 获取角色信息
    public SRet getInfo(Integer accountId) {
        QueryWrapper<DtoPlayer> wrapper = new QueryWrapper<>();
        wrapper.eq("account_id", accountId);
        DtoPlayer dtoPlayer = playerMapper.selectOne(wrapper);

        int playerId = dtoPlayer == null ? createPlayer(accountId) : dtoPlayer.getId();

        // 将角色信息加入cache
        initCache(playerId);
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        eventMgr.sendEvent(EEvent.EVENT_ON_PLAYER_LOGIN, playerInfo);

        String playerToken = GameJwtUtils.createPlayerToken(playerId);
        playerInfo.setToken(playerToken);

        return SRet.success(getCacheInfo(playerInfo));
    }

    public SGetInfo getCacheInfo(PlayerInfo playerInfo) {
        PlayerData playerData = playerInfo.getPlayerData();

        // 向客户端返回角色信息
        SGetInfo sGetInfo = new SGetInfo();
        sGetInfo.setId(playerInfo.getId());
        sGetInfo.setNickname(playerInfo.getNickname());
        sGetInfo.setHead(playerData.getHead());
        sGetInfo.setLv(playerData.getLv());
        sGetInfo.setExp(playerData.getExp());
        sGetInfo.setMoney(playerData.getMoney());
        sGetInfo.setCoin(playerData.getCoin());
        sGetInfo.setGuanqia(playerData.getGuanqia());
        sGetInfo.setNewToken(playerInfo.getToken());
        sGetInfo.setDefLineup(playerData.getDefLineupHeroIds());
        sGetInfo.setLastHangTime(playerData.getLastHangTime());
        sGetInfo.setServerTime(TimeUtils.unixTime());
        sGetInfo.setCombatVal(playerData.getCombatVal());

        return sGetInfo;
    }

    // 处理心跳
    public void procHeartBeat(Integer playerId) {
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        long curTime = System.currentTimeMillis();
        playerInfo.setLastHeartBeatTime(curTime);
    }

    // 增加或删除元宝
    public boolean addCoin(Integer playerId, Integer coin) {
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        if (coin > 0) {
            EProp.Coin.gain(playerInfo, coin);
            return true;
        }

        return EProp.Coin.consume(playerInfo, coin);
    }

    // 增加或删除普通货币
    public boolean addMoney(Integer playerId, Integer money) {
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        if (money > 0) {
            EProp.Money.gain(playerInfo, money);
            return true;
        }

        return EProp.Money.consume(playerInfo, money);
    }

    public Integer getCoin(Integer playerId) {
        return playerCache.getPlayerInfo(playerId).getPlayerData().getCoin();
    }

    // 加载数据库信息到缓存
    private void initCache(int playerId) {
        // 加载用户基础信息到缓存
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId, false);
        if (playerInfo != null) {
            return;
        }

        DtoPlayer dtoPlayer = playerMapper.selectById(playerId);
        playerInfo = new PlayerInfo();
        playerInfo.loadFromDto(dtoPlayer);

        // 初始化心跳时间
        playerInfo.setLastHeartBeatTime(System.currentTimeMillis());

        playerCache.addPlayerInfo(playerInfo);
    }

    public void passGuanQia(Integer playerId) {
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        int curGQ = playerInfo.getPlayerData().getGuanqia();
        if (curGQ >= MainGuanqianMould.maxGuanqia()) {
            return;
        }

        playerInfo.getPlayerData().setGuanqia(playerInfo.getPlayerData().getGuanqia() + 1);
        eventMgr.sendEvent(EEvent.EVENT_UPDATE_GUANQIA, playerId, playerInfo.getPlayerData().getGuanqia());
    }

    // 获取关卡挂机奖励
    public SGetHangAward getHangAward(Integer playerId) {
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        PlayerData playerData = playerInfo.getPlayerData();
        int curTime = TimeUtils.unixTime();
        int d = curTime - playerData.getLastHangTime();
        if (d < 0 || playerData.getLastHangTime() <= 0) {
            d = 0;
        }

        Integer maxHangTime = SpringUtil.getBean(GlobalConfig.class).getIntValue(GlobalConfig.HANG_TIME_MAX);
        if (d > maxHangTime) {
            d = maxHangTime;
        }

        List<DItem> awards = new ArrayList<>();

        if (d > 0) {
            MainGuanqianMould cfg = SpringUtil.getBean(Cfg.class).getCfg(MainGuanqianMould.class, playerData.getGuanqia());
            awards = cfg.getHangAwards(d);
            itemService.addItemBatch(playerId, awards);

            // 增加角色经验
            addExp(playerId, cfg.getAddExp(d));
            addCoin(playerId, cfg.getAddCoin(d));
        }

        playerData.setLastHangTime(curTime);

        SGetHangAward msg = new SGetHangAward();
        msg.setAwards(awards);
        msg.setNewLv(playerData.getLv());
        msg.setNewExp(playerData.getExp());
        return msg;
    }

    public void setLineup(Integer playerId, List<Integer> heroIds) {
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        playerInfo.getPlayerData().setDefLineupHeroIds(heroIds);
        eventMgr.sendEvent(EEvent.EVENT_UPDATE_DEF_LINEUP, playerId, heroIds);
    }

    // 增加角色经验
    public void addExp(Integer playerId, int exp) {
        PlayerData playerData = playerCache.getPlayerInfo(playerId).getPlayerData();
        int curLv = playerData.getLv();
        int curExp = playerData.getExp();
        int remainExp = exp;

        int addLv = 0;
        int index = 0;
        final int MAX_INDEX = 9999;

        while (remainExp > 0) {
            index++;
            if (index > MAX_INDEX) {
                throw new RuntimeException("经验计算异常");
            }

            if (curLv + addLv >= PlayerExpMould.maxLv()) {
                // 最高等级
                curExp += remainExp;
                addLv = PlayerExpMould.maxLv() - curLv;
                break;
            }

            PlayerExpMould cfg = SpringUtil.getBean(Cfg.class).getCfg(PlayerExpMould.class, curLv + addLv);
            if (cfg.getExp() <= curExp + remainExp) {
                // 升级
                remainExp = remainExp - (cfg.getExp() - curExp);
                addLv++;
                curExp = 0;
            } else {
                curExp += remainExp;
                remainExp = 0;
            }
        }

        playerData.setExp(curExp);
        if (addLv > 0) {
            lvUp(playerId, addLv);
        }
    }

    public void lvUp(Integer playerId, int addLv) {
        PlayerInfo playerInfo = playerCache.getPlayerInfo(playerId);
        playerInfo.getPlayerData().setLv(playerInfo.getPlayerData().getLv() + addLv);

        // 更新排行榜
        eventMgr.sendEvent(EEvent.EVENT_UPDATE_LV, playerId, playerInfo.getPlayerData().getLv());
    }
}
